#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE);}while(0);

void do_service(int conn)
{
    char recvbuf[1024];
    memset(recvbuf,0,sizeof(recvbuf));
    int ret ;
    while(1)
    {
        ret = read(conn,recvbuf,sizeof(recvbuf));
        if( ret == 0)
        {
            printf("客户端关闭\n");
            break;
        }
        else if( ret == -1)
        {
            ERR_EXIT("read");
        }
        
        printf("%s\n",recvbuf);
        write(conn,recvbuf,strlen(recvbuf));
        memset(recvbuf,0,sizeof(recvbuf));
    }
}

/**
 * 1.建立一个套接字
 * 2.监听一个套接字
 * 3.accept一个套接字，即监听到有连接到来后，会产生一个新的套接字
 * 4.数据内容读或者写
*/

int main(void)
{
    //声明一个监听的套接字
    int listenfd;
    int ret;
    int conn;
    //监听套接字
    if( (listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))== -1)
        ERR_EXIT("listen");

    struct sockaddr_in servaddr;
    memset(&servaddr,0,sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6666);
    servaddr.sin_addr.s_addr = inet_addr("172.17.215.49");
    //开启地址重复利用
    int on =1;
    if( setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))== -1 )
        ERR_EXIT("socketopt");
    ret = 0;
    ret = bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(ret == -1)
        ERR_EXIT("bind");
    //监听套接字
    ret = 0;
    ret = listen( listenfd,SOMAXCONN);
    if( ret == -1)
        ERR_EXIT("listen");
        //一旦连接成功客户机的地址信息保存在peeraddr中
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof(peeraddr);
    pid_t pid;
    while (1)
    {
    //连接到来后会产生新的套接字 
    conn = accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen);
    if( conn<0 )
        ERR_EXIT("accept");
    //打印客户端的IP和端口号
    printf("ip = %s, port = %d \n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
    pid = fork();
    if( pid == -1)
        ERR_EXIT("fork");
    if( pid == 0 )
    {
        close(listenfd);
        do_service(conn);
        exit(EXIT_SUCCESS);
    }
    else
    {
        close(conn);
    }
    }  
    return 0;
}
